﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Citrix.XaAdminMobile.ViewModel;

namespace Citrix.XaAdminMobile.ViewTablet
{
    /// <summary>
    /// Interaction logic for TabletLayout.xaml
    /// </summary>
    public partial class TabletLayout : UserControl
    {
        /// <summary>
        /// The navigation context is used to manage the lifetime of views.
        /// </summary>
        private class ViewNavContext
        {
            /// <summary>
            /// This activation function is called when the view-model associated with this nav
            /// context becomes active. The v-m and nav context are supplied as the input arguments,
            /// the output being an instance of the page/view to be displayed.
            /// </summary>
            public Func<IPage, ViewNavContext, UserControl> Activate
            {
                get;
                set;
            }

            /// <summary>
            /// Used to optionally cache the view instance for reuse in the nav context.
            /// </summary>
            public UserControl View
            {
                get;
                set;
            }
        }

        /// <summary>
        /// A map of v-m types to their matching navigation contexts.
        /// </summary>
        private Dictionary<Type, ViewNavContext> navContexts = new Dictionary<Type, ViewNavContext>();

        /// <summary>
        /// The root view-model object.
        /// </summary>
        private ViewModelManager vmRoot = null;

        /// <summary>
        /// A reference to the associated navigation manager for the tablet layout.
        /// </summary>
        private TabletNavigation navigation;

        /// <summary>
        /// Our handler for the weak ref event PhoneNavigation.PageChanged
        /// </summary>
        private EventHandler pageChangedHandler;

        /// <summary>
        /// Default ctor.
        /// </summary>
        public TabletLayout()
        {
            SetupNavContexts();
            InitializeComponent();

            this.vmRoot = App.TypedApp.ViewModel;
            this.navigation = this.vmRoot.ActiveNavigation as TabletNavigation;
            if (null != this.navigation)
            {
                this.pageChangedHandler = new EventHandler(Navigation_PageChanged);
                this.navigation.PageChanged += this.pageChangedHandler;
                ApplyView();
            }
            else
            {
                throw new InvalidOperationException();
            }
        }

        /// <summary>
        /// Our handler for the page being changed, used to trigger a swap to the appropriate
        /// view for the new page.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Navigation_PageChanged(object sender, EventArgs e)
        {
            ApplyView();
        }

        /// <summary>
        /// Gets the active pages from the navigation manager and displays the appropriate views
        /// needed to display those pages.
        /// </summary>
        private void ApplyView()
        {
            // Setup the left hand column
            IPage leftPageVm = this.navigation.LeftPage;
            UserControl leftView = GetViewFromVm(leftPageVm);
            if (null != leftView)
            {
                leftView.DataContext = leftPageVm;
            }
            this.LeftPageTarget.Content = leftView;

            // Setup the right hand column
            IPage rightPageVm = this.navigation.RightPage;
            UserControl rightView;
            if (null == rightPageVm)
            {
                // When there is no page to display, we try to show a special "blank" page.
                var pageInfo = GetBlankPage(leftPageVm);
                rightView = pageInfo.Item1;
                rightPageVm = pageInfo.Item2;
            }
            else
            {
                rightView = GetViewFromVm(rightPageVm);
            }

            if (null != rightView)
            {
                rightView.DataContext = rightPageVm;
            }
            this.RightPageTarget.Content = rightView;
        }

        /// <summary>
        /// Creates the view and view-model needed to show a blank page for the supplied left
        /// hand page.
        /// </summary>
        /// <param name="leftPage">The left-hand page.</param>
        /// <returns>The view and view-model.</returns>
        private Tuple<UserControl, IPage> GetBlankPage(IPage leftPage)
        {
            UserControl view = new BlankPageView();
            IPage viewModel = null;

            Type leftPageType = leftPage.GetType();

            if (typeof(SettingsViewModel) == leftPageType)
            {
                viewModel = new BlankPageViewModel(this.vmRoot) { Message = Properties.Resources.BlankPageSettingsMessage };
            }
            else if (typeof(SessionsViewModel) == leftPageType)
            {
                viewModel = new BlankPageViewModel(this.vmRoot) { Message = Properties.Resources.BlankPageSessionsMessage };
            }
            else if (typeof(ServersViewModel) == leftPageType)
            {
                viewModel = new BlankPageViewModel(this.vmRoot) { Message = Properties.Resources.BlankPageServersMessage };
            }
            else
            {
                System.Diagnostics.Debug.Assert(false, "Unknown page");
            }

            return new Tuple<UserControl, IPage>(view, viewModel);
        }

        /// <summary>
        /// Given a view-model object used to display a page, this function returns an instance
        /// of the appropriate view needed to display that page.
        /// </summary>
        /// <param name="page">The view-model object for a page.</param>
        /// <returns>The view to display the supplied page.</returns>
        private UserControl GetViewFromVm(IPage page)
        {
            if (null != page)
            {
                ViewNavContext leftContext = this.navContexts[page.GetType()];
                return leftContext.Activate(page, leftContext);
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Creates all the navigation contexts that are used to create the views that match
        /// any given view-model that is set as the current page.
        /// </summary>
        /// <remarks>
        /// The longer lived views that are commonly used get cached. For other views we
        /// recreate them on the fly.
        /// </remarks>
        private void SetupNavContexts()
        {
            this.navContexts[typeof(SessionsViewModel)] = new ViewNavContext
            {
                Activate = (page, thisRef) =>
                {
                    if (null == thisRef.View)
                    {
                        thisRef.View = new ViewCommon.SessionsView();
                    }
                    return thisRef.View;
                }
            };
            this.navContexts[typeof(ServersViewModel)] = new ViewNavContext
            {
                Activate = (page, thisRef) =>
                {
                    if (null == thisRef.View)
                    {
                        thisRef.View = new ViewCommon.ServersView();
                    }
                    return thisRef.View;
                },
            };
            this.navContexts[typeof(SettingsViewModel)] = new ViewNavContext
            {
                Activate = (page, thisRef) =>
                {
                    return new ViewCommon.SettingsView();
                }
            };
            this.navContexts[typeof(AddServerViewModel)] = new ViewNavContext
            {
                Activate = (page, thisRef) =>
                {
                    return new ViewCommon.AddServerView();
                }
            };
            this.navContexts[typeof(SessionViewModel)] = new ViewNavContext
            {
                Activate = (page, thisRef) =>
                {
                    return new SessionDetailsView();
                }
            };
            this.navContexts[typeof(ServerViewModel)] = new ViewNavContext
            {
                Activate = (page, thisRef) =>
                {
                    return new ServerDetailsView();
                }
            };
        }
    }
}
